package pathfinder;

import java.util.*;

public class Scheduler {

  Factory factory;
  ArrayList<Integer> resTime;
  ArrayList<Integer> time; // time[i] = waktu mesin ke-i datang, berarti sebanyak mesin yang dipinjam
  ArrayList<Integer> machines; // machines[i] = mesin pada jam ke-i, berarti sebanyak jam operasi
  public ArrayList<Integer> products; // produtcts[i] = banyak produk ke-i di akhir penjadwalan
  public ArrayList<Log> log;
  public LinkedList<Point> path;
  List<String> chromosomeArray;
  List<String> chromosomeArrayUnique;
  int profit, timeCounter, money;
  int rentMoney;
  boolean possible; // jadwal ini bisa ?

  public Scheduler() {
    time = new ArrayList<>();
    machines = new ArrayList<>();
    profit = 0;
    timeCounter = 0;
    money = 0;
    rentMoney = 0;
  }

  public Scheduler(Factory f) {
    factory = f;
    time = new ArrayList<>();
    machines = new ArrayList<>();
    profit = 0;
    timeCounter = 0;
  }

  // cari waktu untuk jemput mesin2 itu, state adalah per jam
  public Scheduler(Factory f, String chromosome, int money, int algo) {
    // parse chromosome, cari uniquenya
    factory = f;
    resTime = new ArrayList<>();
    time = new ArrayList<>();
    machines = new ArrayList<>();
    products = new ArrayList<>();
    chromosomeArray = new ArrayList<>();
    chromosomeArrayUnique = new ArrayList<>();
    path = new LinkedList<>();

    profit = 0;
    timeCounter = 0;
    this.money = money;
    rentMoney = 0;
    int count = 0;

    chromosomeArray = Arrays.asList(chromosome.split("\\s+"));
    boolean[] used = new boolean[31];
    Arrays.fill(used, false);

    for (int i = 0; i < chromosomeArray.size(); ++i) {
      int id = Integer.parseInt(chromosomeArray.get(i));
      machines.add(id);
      
      if (!used[id]) {
        chromosomeArrayUnique.add(chromosomeArray.get(i));
        used[id] = true;
      }
    }

//    System.out.println("cAU = " + chromosomeArrayUnique.size());

    for (int x = 0; x < chromosomeArrayUnique.size(); x++) {
      int machineNumber = Integer.parseInt(chromosomeArrayUnique.get(x));
      Vendor v = f.truk.getShortestVendor(machineNumber);
//      System.out.println("MONEY = " + this.money);
      if (f.typeofmachine.get(machineNumber - 1).R <= this.money) {
        this.money -= f.typeofmachine.get(machineNumber - 1).R;
        rentMoney += f.typeofmachine.get(machineNumber - 1).R;
      } else {
        continue;
      }
//      System.out.println(x + " " + f.truk.xCur + " " + f.truk.yCur + " " + v.x + " " + v.y);
      if (f.truk.YCur != 0) {
        if (algo == 0) {
          timeCounter += f.truk.breadhFirstSearch(f.truk.xCur, f.truk.yCur, v.x, v.y);
        } else if (algo == 1) {
          timeCounter += f.truk.depthFirstSearch(f.truk.xCur, f.truk.yCur, v.x, v.y);
        } else if (algo == 2) {
          timeCounter += f.truk.aStar(f.truk.xCur, f.truk.yCur, v.x, v.y);
        }
        if (f.truk.YCur == 0 || x == chromosomeArrayUnique.size() - 1) {
          if (algo == 0) {
            timeCounter += f.truk.breadhFirstSearch(f.truk.xCur, f.truk.yCur, f.truk.x, f.truk.y);
          } else if (algo == 1) {
            timeCounter += f.truk.depthFirstSearch(f.truk.xCur, f.truk.yCur, f.truk.x, f.truk.y);
          } else if (algo == 2) {
            timeCounter += f.truk.aStar(f.truk.xCur, f.truk.yCur, f.truk.x, f.truk.y);
          }
          for (int y = 0; y < f.truk.Y; y++) {
            time.add(timeCounter);
            count++;
          }
        }
      }
    }
  }

  public void print() {
    System.out.println("TIME");
    for (int x = 0; x < time.size(); x++) {
      System.out.println(time.get(x));
    }
    System.out.println("CHROMOSOME UNIQUE");
    for (int x = 0; x < chromosomeArrayUnique.size(); ++x) {
      System.out.println(chromosomeArrayUnique.get(x));
    }
    System.out.println("MACHINES");
    for (int x = 0; x < machines.size(); ++x) {
      System.out.println(x + " " + machines.get(x));
    }
    for (int i = 0; i < factory.typeofmachine.size(); ++i) {
      int T = factory.typeofmachine.get(i).T;
      int W = factory.typeofmachine.get(i).W;
      int S = factory.typeofmachine.get(i).S;
      int P = factory.typeofmachine.get(i).P;
      int I = factory.typeofmachine.get(i).I;
      int C = factory.typeofmachine.get(i).C;
      int R = factory.typeofmachine.get(i).R;
      int D = factory.typeofmachine.get(i).D;
      System.out.println(T + " " + W + " " + S + " " + P + " " + I + " " + C + " " + R + " " + D);
    }

    if (possible) {
      System.out.println("IT'S POSSIBLE!!!!!!!!!!!!");
    } else {
      System.out.println("IMPOSSIBRUUU!!!!!!!!!!!!");
    }
  }

  // Menghitung Fitness Function
  // Jadwal valid kalo possible = true, terus nilai fitness nya diambil dari nilai profit
  public void calculateProfit() {
    // statenya jadinya per jam
    // machines[i] jadi mesin yang dipake pada jam ke-i
    
    possible = true;
    for (int x = 0; x < machines.size(); x++) {
      Integer mesin = machines.get(x);

//      System.out.println(x + " MESIN = " + mesin);
      int idx = chromosomeArrayUnique.indexOf(mesin.toString());
      if (idx > time.size() - 1) { // mesin ga ada
//        System.out.println("EE GA BOLEH");
        possible = false;
        break;
      }

      if (time.get(idx) > x * 60 + time.get(0)) { // mesin belum datang
//        System.out.println("EE GA BOLEH 2");
        possible = false;
        break;
      }

      if ((x + 1) * 60 + time.get(0) > factory.H * 60) { // jam kerja abis
//        System.out.println("FACTORY H = " + factory.H * 60);
//        System.out.println("WAKTU YG DIPERLUKAN = " + ((x + 1) * 60 + time.get(0)));
//        System.out.println("EE GA BOLEH 3");
        possible = false;
        break;
      }

      --mesin; // 0-based index
      int T = factory.typeofmachine.get(mesin).T;
      int W = factory.typeofmachine.get(mesin).W;
      int S = factory.typeofmachine.get(mesin).S;
      int P = factory.typeofmachine.get(mesin).P;
      int I = factory.typeofmachine.get(mesin).I;
      int C = factory.typeofmachine.get(mesin).C;
      int R = factory.typeofmachine.get(mesin).R;
      int D = factory.typeofmachine.get(mesin).D;

      int curT = factory.typeofmachine.get(mesin).curT;
      int curW = factory.typeofmachine.get(mesin).curW;
      int curS = factory.typeofmachine.get(mesin).curS;
      int curP = factory.typeofmachine.get(mesin).curP;

//      System.out.println(T + " " + W + " " + S + " " + P + " " + I + " " + C + " " + R + " " + D);
//      System.out.println(curT + " " + curW + " " + curS + " " + curP);

      for (int i = 0; i < factory.typeofmachine.size(); ++i) {
        if (factory.typeofmachine.get(i).curS > 0) {
          factory.typeofmachine.get(i).setCurS(curS - 1);
        }
      }

      if (curS > 0) { // harusnya masi istirahat
//        System.out.println("EE GA BOLEH 0");
        possible = false;
        break;
      }

      if (curT < T) { // ok, mesin ini masih boleh kerja
        if (D != -1) { // jika dependen
          if (factory.typeofmachine.get(D - 1).curP >= I) {
            factory.typeofmachine.get(D - 1).setCurP(factory.typeofmachine.get(D - 1).curP - I);
          } else { // ga bisa dipake dong
//            System.out.println("EE GA BOLEH 4");
            possible = false;
            break;
          }
        }

        boolean penalty = false;
        if (curW > W && x + 1 < machines.size() && machines.get(x + 1) != mesin + 1) { // penalti
          penalty = true;
        }

        factory.typeofmachine.get(mesin).setCurP(curP + P); // mesin ini memproduksi
        factory.typeofmachine.get(mesin).setCurT(curT + 1); // update beban kerja
        if (penalty) {
          factory.typeofmachine.get(mesin).setCurP((C * P) / 100);
        }

        if (x + 1 < machines.size() && machines.get(x + 1) == mesin + 1) {
          factory.typeofmachine.get(mesin).setCurW(curW + 1);
        } else {
          factory.typeofmachine.get(mesin).setCurW(0);
          factory.typeofmachine.get(mesin).setCurS(S);
        }
      } else {
//        System.out.println("EE GA BOLEH 5");
        possible = false;
        break;
      }
    }

    if (possible) {
//      System.out.println("MASUK");
      for (int i = 0; i < factory.typeofmachine.size(); ++i) {
        profit += factory.typeofmachine.get(i).curP * factory.typeofmachine.get(i).productPrice;
      }
      profit -= rentMoney;
      
      for(int i = 0; i < factory.typeofmachine.size(); ++i) {
        products.add(factory.typeofmachine.get(i).curP);
      }
      
    }

//    System.out.println("PROFIT " + profit);

//    System.out.println("PRODUCTION");
//    for (int i = 0; i < factory.typeofmachine.size(); ++i) {
//      System.out.println(factory.typeofmachine.get(i).curP + " " + factory.typeofmachine.get(i).productPrice);
//    }

    for(int i = 0; i < chromosomeArray.size(); ++i) {
      resTime.add(time.get(0) + i*60);
    }
    
  }

  public void calculateLog() {
    int waktu = 0, waktuKerja = 60 * factory.H;
    int curMachine = 0;
    log = new ArrayList<>();
    StringBuilder sb = new StringBuilder();
    for (int x = 0; x < chromosomeArrayUnique.size(); x++) {
      sb.append("Machine ");
      sb.append(chromosomeArrayUnique.get(x));
      sb.append(" arrived at factory");
      log.add(new Log(time.get(x), sb.toString()));
      sb.delete(0, sb.length());
    }
    waktu = time.get(0);
    int count = 0;
    while (waktu < waktuKerja && count < chromosomeArray.size()) {
      waktu += 60;
      curMachine = Integer.parseInt(chromosomeArray.get(count)) - 1;
      sb.append("Machine ");
      sb.append((int)(curMachine + 1));
      sb.append(" produce ");
//      System.out.println("currrrrrrrr " + curMachine);
      sb.append(factory.typeofmachine.get(curMachine).P);
      sb.append(" products");
      count++;
      log.add(new Log(waktu, sb.toString()));
      sb.delete(0, sb.length());
    }
    log.add(new Log(0, "Factory is opened"));
    log.add(new Log((waktuKerja), "Factory is closed"));
    Collections.sort(log);
  }
}
